package gameEngine

import (
	"MetroBuilderGameJam/gameEngine/drawer"
	"MetroBuilderGameJam/gameEngine/geom"
	"MetroBuilderGameJam/gameEngine/render"
	"MetroBuilderGameJam/gameEngine/utils"
	"MetroBuilderGameJam/world"
	"github.com/hajimehoshi/ebiten/v2"
	"github.com/hajimehoshi/ebiten/v2/ebitenutil"
	"github.com/hajimehoshi/ebiten/v2/vector"
	"image"
)

type Viewer struct {
	windowSize            geom.IntSize
	branchRenderer        *render.Branch
	stantionRenderer      *render.Station
	passengerRenderer     *render.Passenger
	colliderRenderer      *render.Collider
	debugRenderer         *render.Debug
	trainRenderer         *render.Train
	trainPlanningRenderer *render.TrainPlanning
	camera                *Camera
	canvas                *ebiten.Image
	HoverItem             utils.ItemFigure
	DebugEnabled          bool
	DebugConsoleOutput    string
}

type container interface {
	GetBranches() []world.RailRoadBranch
	GetStations() []*world.Station
	GetTrains() []*world.Train
	GetTrainPlans() []*world.TrainPlan
	GetGameOverStation() *world.Station
}

func InitViewer(width, height int) *Viewer {
	rect := image.Rect(0, 0, 2*width, 2*height)
	opts := &ebiten.NewImageOptions{}

	v := &Viewer{
		windowSize: geom.IntSize{Width: width, Height: height},
		canvas:     ebiten.NewImageWithOptions(rect, opts),
	}

	ebiten.SetWindowSize(width, height)

	v.camera = &Camera{
		ViewPort:   geom.Size{Width: float64(width), Height: float64(height)},
		Position:   geom.Point{X: float64(width / 2), Y: float64(height / 2)},
		ZoomFactor: 0,
		Rotation:   0,
	}

	d := drawer.NewCentralDrawer()
	v.branchRenderer = render.NewBranch(d, 30, 10, 6)
	v.stantionRenderer = render.NewStationRenderer(d, 10, 2, drawer.ColorGray, 15)
	v.trainRenderer = render.NewTrainRenderer(d, geom.Size{Width: 15, Height: 8}, 6)
	v.trainPlanningRenderer = render.NewTrainPlanningRenderer(d, geom.Size{Width: 15, Height: 8}, 6)
	v.passengerRenderer = render.NewPassengerRenderer(d, 5, drawer.ColorGray)
	v.colliderRenderer = render.NewColliderRenderer(d)
	v.debugRenderer = &render.Debug{}

	v.trainRenderer.SetPassengerRender(render.NewPassengerRenderer(d, 4, drawer.ColorGray))

	utils.CommonTimer().AddCommand(1000, func() {
		if v.camera.ZoomFactor > -40000 {
			v.camera.ZoomFactor -= 10
		}
	})

	return v
}

func (v *Viewer) Draw(world container, screen *ebiten.Image) []utils.ItemFigure {
	v.canvasClear()
	figures := []utils.ItemFigure{}

	for _, branch := range world.GetBranches() {
		figure := v.branchRenderer.Draw(v.canvas, branch)
		if figure != nil {
			figures = append(figures, figure)
		}
	}

	for _, station := range world.GetStations() {
		figures = append(figures, v.stantionRenderer.Draw(v.canvas, station))
	}

	for _, station := range world.GetStations() {
		v.passengerRenderer.DrawForStation(v.canvas, station)
	}

	for _, train := range world.GetTrains() {
		figures = append(figures, v.trainRenderer.Draw(v.canvas, train))
	}

	for _, train := range world.GetTrainPlans() {
		figures = append(figures, v.trainPlanningRenderer.Draw(v.canvas, train))
	}

	if v.HoverItem != nil {
		if _, ok := v.HoverItem.GetCollider().(*geom.ColliderSet); !ok {
			v.colliderRenderer.Draw(v.canvas, v.HoverItem.GetCollider())
		}
	}

	v.debugRenderer.Draw(v.canvas)

	//ebitenutil.DebugPrint(screen, fmt.Sprintf("Zoom: %d, CanvasSizeOnScreen: %f, %f", v.camera.ZoomFactor, v.CanvasSizeOnScreen().Width, v.CanvasSizeOnScreen().Height))
	//fmt.Println(fmt.Sprintf("Zoom: %d, CanvasSizeOnScreen: %f, %f", v.camera.ZoomFactor, v.CanvasSizeOnScreen().Width, v.CanvasSizeOnScreen().Height))

	gos := world.GetGameOverStation()
	if gos != nil {
		if v.camera.ZoomFactor < 40000 {
			v.camera.ZoomFactor += 300
		}
		pos := v.camera.WorldToScreen(*gos.Position()).AsPoint().AsVec2()
		v.camera.Position = v.camera.Position.AsVec2().Add(pos.Sub(v.camera.Position.AsVec2()).Normalize()).AsPoint()
	}

	v.camera.Render(v.canvas, screen)
	if v.DebugEnabled {
		vector.DrawFilledRect(screen, 0, 0, 600, 30, drawer.ColorBlack, false)
		ebitenutil.DebugPrint(screen, v.DebugConsoleOutput)
	}

	return figures
}

func (v *Viewer) CanvasSize() geom.Size {
	s := v.canvas.Bounds().Size()
	return geom.Size{Width: float64(s.X), Height: float64(s.Y)}
}

func (v *Viewer) CanvasSizeOnScreen() geom.Size {
	pEnd, err := v.camera.ScreenToWorld(v.windowSize.AsVec2().AsPoint().ToIntPoint())
	if err != nil {
		return v.camera.ViewPort
	}
	pBegin, err := v.camera.ScreenToWorld(geom.IntPoint{})
	if err != nil {
		return v.camera.ViewPort
	}
	return geom.Size{Width: pEnd.X - pBegin.X, Height: pEnd.Y - pBegin.Y}
}

func (v *Viewer) canvasClear() {
	//v.canvas.Clear()
	v.canvas.Fill(drawer.ColorBiege)
}

func (v *Viewer) Center() geom.Point {
	p, err := v.camera.ScreenToWorld(v.windowSize.AsVec2().Div(2).AsPoint().ToIntPoint())
	if err != nil {
		return geom.Point{}
	}

	return p
}

func (v *Viewer) WindowPointToWorld(point geom.IntPoint) (geom.Point, error) {
	return v.camera.ScreenToWorld(point)
}
